/**
 * Alipay.com Inc. Copyright (c) 2004-2018 All Rights Reserved.
 */
package com.yeyks.service.impl;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.yeyks.base.Arguments;
import com.yeyks.base.PageInfo;
import com.yeyks.dal.dao.*;
import com.yeyks.dal.domain.AccountInfo;
import com.yeyks.dal.domain.HotelInfo;
import com.yeyks.dal.domain.RoomInfo;
import com.yeyks.dal.domain.UserInfo;
import com.yeyks.dto.AccountDTO;
import com.yeyks.exception.PermissionInvalidException;
import com.yeyks.exception.ServiceException;
import com.yeyks.exception.TokenInvalidException;
import com.yeyks.model.HotelMgrExtraInfo;
import com.yeyks.model.em.AccountType;
import com.yeyks.model.em.ResponseStatus;
import com.yeyks.service.AccountService;
import com.yeyks.util.ObjectConvertUtil;
import com.yeyks.util.Utility;
import com.yeyks.model.HotelMgrExtraInfo;
import com.yeyks.model.em.AccountType;
import com.yeyks.model.em.ResponseStatus;
import com.yeyks.service.AccountService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 账户服务接口实现
 * @author Beacon
 * @version $Id: AccountServiceImpl.java, v 0.1 2018年07月31日 19:56 Beacon Exp $
 */
@Slf4j
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    AccountInfoMapper accountInfoMapper;

    @Autowired
    HotelInfoMapper hotelInfoMapper;

    @Autowired
    RoomInfoMapper  roomInfoMapper;

    @Autowired
    UserInfoMapper userInfoMapper;

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    /** token秘钥 */
    public static final String SECRET = "269e2643-b259-419c-bc4d-23b34d09f9f4";
    /** token 过期时间: 10天 */
    public static final int calendarField = Calendar.DATE;
    public static final int calendarInterval = 100;
//    public static final int calendarField = Calendar.SECOND;
//    public static final int calendarInterval = 20;

    /**
     * 由当前用户ID创建对应Token，有效期为10天（calendarInterval * calendarField)
     * @param user_id
     * @return
     * @throws Exception
     */
    public String createToken(String user_id) {
        Date iatDate = new Date();
        // expire time
        Calendar nowTime = Calendar.getInstance();
        nowTime.add(calendarField, calendarInterval);
        Date expiresDate = nowTime.getTime();

        // header Map
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("alg", "HMAC256");
        map.put("type", "JWT");

        // build token
        // param backups {iss:Service, aud:APP}
        String token = JWT.create().withHeader(map) // header
                .withClaim("user_id", user_id)
                .withIssuedAt(iatDate) // sign time
                .withExpiresAt(expiresDate) // expire time
                .sign(Algorithm.HMAC256(SECRET)); // signature

        return token;
    }

    /**
     * 解密Token，获得内容
     *
     * @param token
     * @return
     * @throws Exception
     */
    public static Map<String, Claim> getClaims(String token) {
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
        DecodedJWT jwt = verifier.verify(token);
        return (jwt == null || jwt.getClaims() == null) ? null : jwt.getClaims();
    }

    /**
     * 根据Token获取user_id
     *
     * @param token
     * @return user_id
     */
    public String getUserId(String token) throws TokenInvalidException {

        String user_id = null;
        try {
            Map<String, Claim> claims = getClaims(token);
            Claim user_id_claim = claims.get("user_id");
            user_id = user_id_claim == null ? null : user_id_claim.asString();
        } catch (Throwable e) {
            e.printStackTrace();
        }

        if (user_id == null) {
            throw new TokenInvalidException();
        }
        return user_id;
    }

    @Override
    public void getPermission(Integer id, String ajax) throws Exception {
        AccountInfo accountInfo = accountInfoMapper.selectByPrimaryKey(id);

        if (accountInfo == null) {
            throw new PermissionInvalidException();
        }

        // TODO: 权限列表
    }

    @Override
    public void getPermission(String token, String ajax) throws Exception {
        getPermission(token2UserId(token), ajax);
    }

    @Override
    public String userId2Token(Integer id) {
        return createToken(id.toString());
    }

    @Override
    public Integer token2UserId(String token) throws TokenInvalidException {
        return Integer.parseInt(getUserId(token));
    }

    @Override
    public HotelInfo userId2HotelInfo(Integer id) {

        HotelInfo hotelInfo = null;
        try {
            AccountInfo accountInfo = accountInfoMapper.selectByPrimaryKey(id);
            if (accountInfo == null) {
                return null;
            }

            if (accountInfo.getType() != AccountType.HotelManager) {
                return null;
            }

            HotelMgrExtraInfo extraInfo = JSON.parseObject(accountInfo.getExtraInfo(), HotelMgrExtraInfo.class);
            if (extraInfo == null || extraInfo.getHotelId() == 0) {
                return null;
            }

            hotelInfo = hotelInfoMapper.selectByPrimaryKey(extraInfo.getHotelId());
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return hotelInfo;
    }

    @Override
    public List<HotelInfo> searchHotelsByOperator(String token) {
        try {
            Integer userId = token2UserId(token);

            return searchHotelsByOperator(userId);
        } catch(Throwable e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public List<HotelInfo> searchHotelsByOperator(Integer id) {
        List<HotelInfo> hotelInfoList = null;
        try {
            hotelInfoList = hotelInfoMapper.selectByOperator(id);
        } catch(Throwable e) {
            e.printStackTrace();
        }
        return hotelInfoList;
    }

    @Override
    public List<HotelInfo> searchHotelsByAdministrator(Integer id) {
        List<HotelInfo> hotelInfoList = null;
        try {
            hotelInfoList = hotelInfoMapper.selectByAdministrator(id);
        } catch(Throwable e) {
            e.printStackTrace();
        }
        return hotelInfoList;
    }

    @Override
    public List<HotelInfo> searchHotelsByAdministrator(List<Integer> ids) {
        List<HotelInfo> hotelInfoList = null;
        try {
            hotelInfoList = hotelInfoMapper.selectByAdministrators(ids);
        } catch(Throwable e) {
            e.printStackTrace();
        }
        return hotelInfoList;
    }

    @Override
    public HotelInfo searchHotelByOperator(Integer id) {
        List<HotelInfo> hotelInfos = searchHotelsByOperator(id);
        return (hotelInfos == null || hotelInfos.size() == 0) ? null : hotelInfos.get(0);
    }

    @Override
    public HotelInfo searchHotelByOperator(String token) {
        List<HotelInfo> hotelInfos = searchHotelsByOperator(token);
        return (hotelInfos == null || hotelInfos.size() == 0) ? null : hotelInfos.get(0);
    }

    @Override
    public HotelInfo searchHotelByOperator(String token, Integer hotelId) {
        List<HotelInfo> hotelInfoList = this.searchHotelsByOperator(token);
        for (HotelInfo hotelInfo : hotelInfoList) {
            if (hotelInfo.getId().intValue() == hotelId.intValue()) {
                return hotelInfo;
            }
        }

        return null;
    }

    @Override
    public boolean roomOperator(String token, Integer roomId) {
        List<HotelInfo> hotelInfoList = this.searchHotelsByOperator(token);

        List<Integer> ids = new ArrayList<>();
        for (HotelInfo hotelInfo : hotelInfoList) {
            ids.add(hotelInfo.getId());
        }

        List<RoomInfo> roomInfoList = roomInfoMapper.selectByHotelIds(ids);
        for (RoomInfo roomInfo : roomInfoList) {
            if (roomInfo.getId() == roomId) {
                return true;
            }
        }

        return false;
    }

    @Override
    public List<RoomInfo> searchRoomByOperator(String token) {
        List<HotelInfo> hotelInfoList = this.searchHotelsByOperator(token);

        List<Integer> ids = new ArrayList<>();
        for (HotelInfo hotelInfo : hotelInfoList) {
            ids.add(hotelInfo.getId());
        }

        return roomInfoMapper.selectByHotelIds(ids);
    }

    @Override
    public boolean roomHavePermission(String token, List<Integer> ids) {

        List<RoomInfo> roomInfoList = searchRoomByOperator(token);

        for (Integer i : ids) {

            boolean found = false;
            for (RoomInfo roomInfo : roomInfoList) {
                if (roomInfo.getId().intValue() == i.intValue()) {
                    found = true;
                    break;
                }
            }

            if (!found) {
                return false;
            }
        }

        return true;
    }

    @Override
    public int token2AccountType(String token) throws Exception {
        AccountInfo accountInfo = token2AccountInfo(token);
        return accountInfo.getType();
    }

    @Override
    public AccountInfo token2AccountInfo(String token) throws Exception {
        Integer userId = token2UserId(token);
        AccountInfo accountInfo =  accountInfoMapper.selectByPrimaryKey(userId);
        if (accountInfo == null) {
            throw new TokenInvalidException();
        }

        return accountInfo;
    }

    @Override
    public UserInfo tokenUserInfo(String token) {
        UserInfo userInfo = null;
        try {
            Integer userId = token2UserId(token);
            userInfo =  userInfoMapper.selectByPrimaryKey(userId);
        } catch (Exception e) {
            log.info("通过用户token获取用户失败，token=" + token + "，错误信息：" + e.getMessage());
        }
        if (userInfo == null) {
            log.info("token过期，请重新登陆，token={}", token);
            throw new ServiceException(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode(), "用户信息失效，请重新登陆");
        }
        return userInfo;
    }

    @Override
    public UserInfo tokenUserInfoNoException(String token) {
        UserInfo userInfo = null;
        try {
            Integer userId = token2UserId(token);
            userInfo =  userInfoMapper.selectByPrimaryKey(userId);
        } catch (Exception e) {
            log.info("通过用户token获取用户失败，token=" + token + "，错误信息：" + e.getMessage());
        }
        if (userInfo == null) {
            log.info("token过期，请重新登陆，token={}", token);
        }
        return userInfo;
    }

    @Override
    public AccountInfo token2AccountInfoV2(String token){
        if (Arguments.isEmpty(token)) {
            throw new ServiceException("token不能为空");
        }
        AccountInfo accountInfo = null;
        try {
            Integer userId = token2UserId(token);
            accountInfo =  accountInfoMapper.selectByPrimaryKey(userId);
        } catch (Exception e) {
            log.info("通过用户token获取用户失败，token=" + token + "，错误信息：" + e.getMessage());
        }
        if (accountInfo == null) {
            log.info("token过期，请重新登陆，token={}", token);
            throw new ServiceException(ResponseStatus.RS_FAIL_INVALID_TOKEN.getCode(), "用户信息失效，请重新登陆");
        }
        return accountInfo;
    }

    @Override
    public void setPrecentage(Integer admin, Integer branch, Integer city) throws Exception {
        /*
        List<AccountInfo> accountInfos = accountInfoMapper.selectByType(AccountType.Administrator);
        if (accountInfos == null || accountInfos.isEmpty()) {
            throw new Exception(ResponseStatus.RS_ACCOUNT_NO_ADMINISTRATOR.getMsg());
        }

        AccountInfo adminAccount = accountInfos.get(0);

        Map<String, Integer> precentageMap = new HashMap<>();
        precentageMap.put("admin", admin);
        precentageMap.put("branch", branch);
        precentageMap.put("city", city);

        String extraInfo = Utility.setExtraInfo(adminAccount.getExtraInfo(), "pcInfo", precentageMap);
        int rec = accountInfoMapper.updateExtraInfoById(adminAccount.getId(), extraInfo);
        if (rec == 0) {
            throw new Exception("updated 0 row.");
        }
        */
        int rec = accountInfoMapper.setPrecentage(admin, branch, city);
        if (rec == 0) {
            throw new Exception("updated 0 row.");
        }
    }

    @Override
    public Map getPrecentage(){
        /*
        List<AccountInfo> accountInfos = accountInfoMapper.selectByType(AccountType.Administrator);
        if (accountInfos == null || accountInfos.isEmpty()) {
            throw new Exception(ResponseStatus.RS_ACCOUNT_NO_ADMINISTRATOR.getMsg());
        }

        AccountInfo adminAccount = accountInfos.get(0);

        String pcInfoString = Utility.getExtraInfo(adminAccount.getExtraInfo(), "pcInfo");
        Map pcInfo = JSON.parseObject(pcInfoString, Map.class);
        return pcInfo;
        */
        List<Map> pcInfo = accountInfoMapper.getPrecentage();
        if (pcInfo == null || pcInfo.size() == 0) {
            return null;
        } else {
            Map<String, Integer> pc = new HashMap<>();
            pc.put("admin", (Integer) pcInfo.get(0).get("per_admin"));
            pc.put("branch", (Integer) pcInfo.get(0).get("per_branch"));
            pc.put("city", (Integer) pcInfo.get(0).get("per_city"));
            return pc;
        }
    }

    @Override
    public List<AccountInfo> getChildAccounts(Integer id) {
        try {
            List<Integer> ids = new ArrayList<>();
            ids.add(id);
            List<AccountInfo> accountInfoList = accountInfoMapper.selectChildren(ids);
            return accountInfoList;
        } catch (Throwable e) {e.printStackTrace();}
        return null;
    }
    @Override
    public List<AccountInfo> getChildAccountsV2(List<Integer> ids) {
        try {
            List<AccountInfo> accountInfoList = accountInfoMapper.selectChildren(ids);
            return accountInfoList;
        } catch (Throwable e) {e.printStackTrace();}
        return null;
    }

    /**
     * 市代理查询城市运营商
     *
     * @param valid    是否有效(默认有效)
     * @param area    省名称
     * @param keyword     关键字
     * @param pageNum  当前页 （默认1）
     * @param pageSize 页码 （默认10）
     * @param operatorType  操作人类型
     * @return
     */
    @Override
    public Map<String, Object> cityAgentList(Boolean valid, String area, String keyword, Integer id, Integer pageNum, Integer pageSize, Integer operatorType, Integer createTime, Integer province) {
        PageInfo pageInfo = PageInfo.of(pageNum, pageSize);
        Map<String, Object> map = new HashMap<>();
        map.put("id", id);
        map.put("area", area);
        map.put("valid", valid);
        map.put("name", keyword);
        map.put("operatorType", operatorType);
        map.put("offset", pageInfo.getOffset());
        map.put("limit", pageInfo.getLimit());
        map.put("createTime", createTime);
        map.put("province", province);
        List<AccountInfo> accountInfos = accountInfoMapper.cityAgentList(map);
        Integer count = accountInfoMapper.cityAgentListCount(map);
        List<AccountDTO> accountDTOS = tranformAccountDTO(accountInfos);
        map.clear();
        map.put("count", count);
        map.put("list", accountDTOS);
        return map;
    }

    private List<AccountDTO> tranformAccountDTO(List<AccountInfo> accountInfos) {
        List<AccountDTO> accountDTOS = new ArrayList<>();
        for (AccountInfo accountInfo : accountInfos) {
            AccountDTO accountDTO = ObjectConvertUtil.convertObject(accountInfo, AccountDTO.class);
            Integer countChildren = accountInfoMapper.countOfChildren(accountInfo.getId(), null, null);
            accountDTO.setGrandchildrenCount(countChildren);
            accountDTOS.add(accountDTO);
        }
        return accountDTOS;
    }

    /**
     * 通过area精准查找
     * @param area
     * @param type
     * @return
     */
    @Override
    public AccountInfo selectAccountByArea(String area, Integer type) {
        Map<String, Object> map = new HashMap<>();
        map.put("type", type);
        map.put("area", area);
        return accountInfoMapper.selectAccountByArea(map);
    }

    /**
     * 将type为6的（即用户）数据迁移到用户表中
     */
    @Override
    @Transactional
    public void testChangeUser() {
        List<AccountInfo> accountInfos = accountInfoMapper.selectByType(AccountType.User);
        for (AccountInfo accountInfo : accountInfos) {
            UserInfo userInfo = ObjectConvertUtil.convertObject(accountInfo, UserInfo.class);
            userInfoMapper.insertReturnId(userInfo);
            Integer userId = userInfo.getId();
            if (userId != null) {
                //查询之前用户的订单，将user_id修改为userId
                orderInfoMapper.updateUserIdByOldUserId(userId, accountInfo.getId());
            }

        }
        log.info("结束");

    }

    /**
     * 查询父id
     *
     * @param id
     * @return
     */
    @Override
    public List<Integer> getParentId(Integer id) {
        return accountInfoMapper.selectAllParentId(id);
    }


}